﻿using System;
using System.Collections.Generic;
using System.Linq;
using XLFrame.NodeEditor.Extensions;

namespace XLFrame.NodeEditor.Interpreter
{
    /// <summary>
    /// 逻辑解释器,模拟运行环境
    /// </summary>
    public class LogicalInterpreter
    {
        private NodeTreeDataBase m_data;
        
        private Dictionary<string,LogicalVirtualNode> m_virtualNodes = new Dictionary<string, LogicalVirtualNode>();
        private List<LogicalVirtualNode> m_virtualNodesList = new List<LogicalVirtualNode>();
        
        public LogicalInterpreter(NodeTreeDataBase data)
        {
            this.m_data = data;
            init();
        }

        private void init()
        {
            CreateNode();
            ConnectNodes();
        }

        /// <summary>
        /// 创建节点
        /// </summary>
        private void CreateNode()
        {
            foreach (var item in m_data.NodeDataBaseDic)
            {
                var obj = new LogicalVirtualNode(item.Value,this);
                m_virtualNodes.Add(item.Key,obj);
                m_virtualNodesList.Add(obj);
            }
        }
        
        /// <summary>
        /// 连接节点端口
        /// </summary>
        private void ConnectNodes()
        {
            foreach (var item in m_virtualNodes)
            {
                var list = item.Value.GetAllOutputPort();
                
                foreach (var port in list)
                {
                    // 获取连接的节点
                    var connectNode = m_data.NodeDataBaseDic[item.Value.GUID].GetOutputPort(port.GUID);
                    if (connectNode != null)
                    {
                        foreach (var link in connectNode.NodeLinkDatas.Values)
                        {
                            var portData = m_virtualNodes[link.InputNodeGuid].GetInputPort(link.PortGuid);
                            portData.Value = port;
                        }
                    }

                }
            }
        }

        
        
        public void Run(string nodeTypeName="开始")
        {
            var logic = m_virtualNodesList.Find(t => 
                                                    t.Attributes.GetAttribute<string>("__节点类型") == nodeTypeName);
            if (logic != null)
            {
                logic.Execute(nodeTypeName);
            }
        }
        
        
        public LogicalVirtualNode GetNode(string guid)
        {
            return m_virtualNodes[guid];
        }
    }

    /// <summary>
    /// 逻辑解释器虚拟节点
    /// </summary>
    public class LogicalVirtualNode
    {
        private NodeDataBase m_dataBase;
        private LogicalInterpreter m_interpreter;
        private Dictionary<string,PortData> m_inputPort = new Dictionary<string, PortData>();
        private Dictionary<string,PortData> m_outputPort = new Dictionary<string, PortData>();
        /// <summary>
        /// 节点注册的逻辑层
        /// </summary>
        private List<INodeLogic> m_nodeLogics = new List<INodeLogic>();
        
        public string GUID => m_dataBase.GUID;
        public string NodeType => m_dataBase.NodeType;
        public AttributesData Attributes => m_dataBase.Attributes;
        
        public LogicalVirtualNode(NodeDataBase dataBase, LogicalInterpreter interpreter)
        {
            this.m_dataBase = dataBase;
            this.m_interpreter = interpreter;
            InitPort();
        }
        
        /// <summary>
        /// 初始化端口
        /// </summary>
        private void InitPort()
        {
            CreatePort(m_dataBase);
            CreateLogic(m_dataBase);
        }

        private void CreatePort(NodeDataBase data)
        {
            PortData createPortData(PortViewData item)
            {
                return new PortData(item.PortName,item.GUID,item.PortType,m_dataBase.Attributes);
            }
            
            // 实例化端口
            foreach (var item in data.GetInputPort())
            {
                var port = createPortData(item);
                m_inputPort.Add(port.GUID,port);
            }
            
            
            foreach (var item in data.GetOutputPort())
            {
                var port = createPortData(item);
                m_outputPort.Add(port.GUID,port);
            }
        }
        private void CreateLogic(NodeDataBase dataBase)
        {
            foreach (var item in dataBase.NodeLogicData)
            {
                var type = Type.GetType(item);
                if(type is null) continue;
                var logic = Activator.CreateInstance(type) as INodeLogic;
                if(logic is null) continue;
                logic.Init(dataBase, GetAllInputPort, GetAllOutputPort, OnNextExecute);
                m_nodeLogics.Add(logic);
            }
        }

        public void Execute(string lineName,string EGuid="")
        {
            if (string.IsNullOrEmpty(EGuid))
            {
                EGuid = Guid.NewGuid().ToString();
            }
            string nameType = ToolExtensions.RemoveNameType(lineName);
            foreach (var logic in m_nodeLogics)
            {
                logic.EGuid = EGuid;
                logic.Execute(nameType);
            }
        }
        
        /// <summary>
        /// 当执行下一个节点逻辑时调用
        /// </summary>
        /// <param name="arg0"></param>
        private void OnNextExecute(string arg0)
        {
            if (m_outputPort.TryGetValue(arg0, out var port))
            {
                var link = m_dataBase.GetOutputPort(port.GUID).NodeLinkDatas;
                if(!link.Any()) return;
                var item = link.Values.First();

                var node = m_interpreter.GetNode(item.InputNodeGuid);
                node.Execute(node.GetInputPort(item.PortGuid).Name);
            }
        }

        private List<PortData> GetAllInputPort()
        { 
            return m_inputPort.Values.ToList();
        }

        /// <summary>
        /// 获取所有输出端口
        /// </summary>
        /// <returns></returns>
        public List<PortData> GetAllOutputPort()
        {
           return m_outputPort.Values.ToList();
        }
        
        public PortData GetInputPort(string guid)
        {
            return m_inputPort[guid];
        }
    }
}